#ifndef PL_PERL_HELPERS_H
#define PL_PERL_HELPERS_H

#include "mb/pg_wchar.h"

#include "plperl.h"


/*
 * 从utf8转换为数据库编码
 *
 * 返回原始字符串的palloc'副本
 */
static inline char *
utf_u2e(char *utf8_str, size_t len)
{
	char	   *ret;

	ret = pg_any_to_server(utf8_str, len, PG_UTF8);

	/* 确保即使没有发生转换我们也有副本 */
	if (ret == utf8_str)
		ret = pstrdup(ret);

	return ret;
}

/*
 * 从数据库编码转换为utf8
 *
 * 返回原始字符串的palloc'副本
 */
static inline char *
utf_e2u(const char *str)
{
	char	   *ret;

	ret = pg_server_to_any(str, strlen(str), PG_UTF8);

	/* 确保即使没有发生转换我们也有副本 */
	if (ret == str)
		ret = pstrdup(ret);

	return ret;
}


/*
 * 将SV转换为当前数据库编码中的char *
 *
 * 返回原始字符串的palloc'副本
 */
static inline char *
sv2cstr(SV *sv)
{
	dTHX;
	char	   *val,
			   *res;
	STRLEN		len;

	/*
	 * 从perl中获取UTF-8编码的char *。*注意*这可能不是有效的utf8！
	 */

	/*
	 * SvPVutf8()在某些情况下会出现严重错误，比如typeglobs和
	 * 只读对象如$^V。这是一个perlbug - 不应该发生。
	 * 为了避免崩溃后端，我们在将其传递给SvPVutf8()之前，
	 * 先复制sv。复制的对象在使用完后会被垃圾回收。
	 */
	if (SvREADONLY(sv) ||
		isGV_with_GP(sv) ||
		(SvTYPE(sv) > SVt_PVLV && SvTYPE(sv) != SVt_PVFM))
		sv = newSVsv(sv);
	else
	{
		/*
		 * 增加引用计数，以便在完成后我们可以简单地使用SvREFCNT_dec()释放
		 */
		SvREFCNT_inc_simple_void(sv);
	}

	/*
	 * 从Perl请求字符串，使用UTF-8编码；但如果我们在一个
	 * SQL_ASCII数据库中，则只请求字节汤而不试图将其转换为
	 * UTF8，因为那可能会失败。
	 */
	if (GetDatabaseEncoding() == PG_SQL_ASCII)
		val = SvPV(sv, len);
	else
		val = SvPVutf8(sv, len);

	/*
	 * 现在转换为数据库编码。我们使用perl的长度，以防我们
	 * 嵌入了一个空字节，以确保我们正确地报错。
	 */
	res = utf_u2e(val, len);

	/* 现在安全地对新的SV进行垃圾回收 */
	SvREFCNT_dec(sv);

	return res;
}

/*
 * 从假定在当前数据库的编码中的字符串创建新的SV。
 */
static inline SV *
cstr2sv(const char *str)
{
	dTHX;
	SV		   *sv;
	char	   *utf8_str;

	/* 在SQL_ASCII时没有转换 */
	if (GetDatabaseEncoding() == PG_SQL_ASCII)
		return newSVpv(str, 0);

	utf8_str = utf_e2u(str);

	sv = newSVpv(utf8_str, 0);
	SvUTF8_on(sv);
	pfree(utf8_str);

	return sv;
}

/*
 * croak()带有指定的消息，消息以数据库编码给出。
 *
 * 理想情况下我们只需编写croak("%s", str)，但普通的croak()
 * 与非ASCII数据不兼容。在现代Perl版本中，我们可以调用cstr2sv()
 * 并将结果传递给croak_sv(); 在没有croak_sv()的版本中，
 * 我们必须更加努力。
 */
static inline void
croak_cstr(const char *str)
{
	dTHX;

#ifdef croak_sv
	/* 使用sv_2mortal()确保临时SV被释放 */
	croak_sv(sv_2mortal(cstr2sv(str)));
#else

	/*
	 * 以旧方式执行此操作是将标记为UTF8的值分配给ERRSV，然后
	 * 调用croak(NULL)。但如果我们让croak()附加错误位置，
	 * 在某些Perl版本中，它会太晚进行（仅在弹出堆栈后）。
	 * 因此，使用mess()创建一个已附加错误位置的信息的SV。
	 */
	SV		   *errsv = get_sv("@", GV_ADD);
	char	   *utf8_str = utf_e2u(str);
	SV		   *ssv;

	ssv = mess("%s", utf8_str);
	SvUTF8_on(ssv);

	pfree(utf8_str);

	sv_setsv(errsv, ssv);

	croak(NULL);
#endif							/* croak_sv */
}

#endif							/* PL_PERL_HELPERS_H */
